package com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.zte.claa.inficombo.csa.app.config.HCGNSSSvrCommProps;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevBaseMapper;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevPagePara;
import com.zte.claa.inficombo.csa.app.model.dev.DevTypeModel;
import com.zte.claa.inficombo.csa.app.model.dev.SensorDevEuiPV;
import com.zte.claa.inficombo.csa.app.model.redis.DevMeteData;
import com.zte.claa.inficombo.csa.app.service.csa.impl.BaseCacheManager;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.ExtRptDataParser;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.RptRawData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss.mina.GnssParseResultAcceptor;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss.mina.msgbean.GnssRequest;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;
import com.zte.claa.inficombo.csa.app.util.SecurityUtil;

/**
 * 第三方需要透传原始数据的的华测GNSS服务实现类。
 * 
 * @author 0283000115
 */
@Service("HCGNSSParserService")
@Lazy(false)
public class HCGNSSParserService implements InitializingBean, DisposableBean, ExtRptDataParser, IGnssParseResultReceiver {	
	// 日志记录
	private final Logger logger = LoggerFactory.getLogger(HCGNSSParserService.class);
	
	@Autowired
	private ParserContainer parserContainer;
	
	@Autowired
	private HCGNSSSvrCommProps gnssCommProps;
	
	@Autowired
	private BaseCacheManager cacheManager;
	
	@Autowired
	private DevBaseMapper devBaseMapper;
	
	/**
	 * deveui --->host:port映射；
	 */
	private Map<String, String> deveui2HostMap = new HashMap<String, String>();
	/**
	 * key: host:port, val: GnssCommService
	 */
	private Map<String, GnssCommService> host2CommSvrMap = new HashMap<String, GnssCommService>();
	
	// 内部分发队列...
	private Thread[] dataDealears = null;
	private LinkedBlockingQueue<RptRawData> queue = null;
	private boolean isWorkOn = true;

	// GNSS解析结果异步接收服务器...
	private GnssParseResultAcceptor resultReceiver = null;
	
	/**
	 * 服务初始化.
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		if("N".equalsIgnoreCase(this.gnssCommProps.getNeedstart().trim())){
			logger.warn("[*@@*]HCGNSSParserService NOT need start...");
			return;
		}
		int pageIdx = 1;
		DevPagePara pp = new DevPagePara();
		pp.setBatch(500);
		pp.setPageIndex(pageIdx);
		List<SensorDevEuiPV> pvList = this.devBaseMapper.querySensorDevEuiPVs(pp);
		boolean hasMore = (pvList != null && pvList.size() > 0);
		while(hasMore){
			for(SensorDevEuiPV pv : pvList){
				this.addSensorDevEuiPV(pv);
			}
			pageIdx++;
			pp.setPageIndex(pageIdx);
			pvList = this.devBaseMapper.querySensorDevEuiPVs(pp);
			hasMore = (pvList != null && pvList.size() > 0);
		}
		// 注册原始数据发送处理器.
		String devtp = this.gnssCommProps.getDevtype().trim();
		this.parserContainer.setExtRptDataDealer(devtp, this);
		logger.info("setExtRptDataDealer for devtp ok: "+devtp);
		// 共用一个缓冲队列.
		this.queue = new LinkedBlockingQueue<RptRawData>(this.gnssCommProps.getQueuemaxsize());
		this.isWorkOn = true;
		int dealerNum = this.gnssCommProps.getProcessornum();
		this.dataDealears = new Thread[dealerNum];
		// 启动分发线程。。。
		for (int i = 0; i < dealerNum; i++) {
			this.dataDealears[i] = new Thread(new Runnable() {
				@Override
				public void run() {
					dealRptRawData();
				}
			});
			this.dataDealears[i].setName("HCGNSSParserService-dispatcher-" + i);
			this.dataDealears[i].start();
		}		
		// 启动异步消息接收器....
		this.resultReceiver = new GnssParseResultAcceptor();
		this.resultReceiver.setGnssComm(this.gnssCommProps);
		this.resultReceiver.setProcessorCount(1);
		this.resultReceiver.setResultReceiver(this);
		this.resultReceiver.startUp();		 
		logger.info("HCGNSSParserService is inited ok!");
	}
	
	/**
	 * 退出销毁资源.
	 */
	@Override
	public void destroy() throws Exception {
		this.isWorkOn = false;
		if(this.queue != null){
			this.queue.clear();
			for(int i=0; i<gnssCommProps.getProcessornum(); i++){
				this.queue.offer(new RptRawData(RptRawData.INVALID_DEVEUI));
			}
		}
		// 关闭所有连接》
		Iterator<String> it = this.host2CommSvrMap.keySet().iterator();
		while(it.hasNext()){
			String key = it.next();
			GnssCommService svr = this.host2CommSvrMap.get(key);
			if(svr != null){
				svr.closeConnectToGnss();
			}
		}
		this.host2CommSvrMap.clear();
		this.deveui2HostMap.clear();
		// 停止接收器.
		if(this.resultReceiver != null){
			this.resultReceiver.shutdown();
			this.resultReceiver = null;
		}
		logger.warn("Service is destroyed!");
	}
	
	/**
	 * 添加SensorDevEuiPV
	 * 
	 * @param pv
	 */
	public void addSensorDevEuiPV(SensorDevEuiPV pv){
		if(pv == null){
			return;
		}
		String hostIp = pv.getHostip();
		int hostPort = pv.getHostport();
		if(hostIp == null || hostIp.trim().equals("") || hostPort == 0){
			logger.warn("invalid SensorDevEuiPV: "+pv);
			return;
		}
		String hostIpPort = hostIp+":"+hostPort;
		this.deveui2HostMap.put(pv.getDeveui(), hostIpPort);
		// 初始化 GnssCommService 连接...
		GnssCommService gnssCommSvr = this.host2CommSvrMap.get(hostIpPort);
		if (gnssCommSvr == null) {
			HCGNSSSvrCommProps prop = this.gnssCommProps.clone();
			prop.setPort(hostPort);
			prop.setSvrip(hostIp);
			gnssCommSvr = new GnssCommServiceImpl(prop);
			this.host2CommSvrMap.put(hostIpPort, gnssCommSvr);
		}
	}
	
	/**
	 * 删除deveui对应的链路.
	 * 
	 * @param pv
	 */
	public String releaseGnssHostConnByDevEui(String deveui){
		if(deveui == null){
			return "deveui is null!";
		}
		String hostIpPort = this.deveui2HostMap.get(deveui);
		if(hostIpPort == null){
			return "hostIpPort is null";
		}
		// 关闭 GnssCommService 连接...
		GnssCommService gnssCommSvr = this.host2CommSvrMap.get(hostIpPort);
		if (gnssCommSvr != null) {
			gnssCommSvr.closeConnectToGnss();
			this.host2CommSvrMap.remove(hostIpPort);
			return "OK";
		}
		return "GnssCommService not find!";
	}

	/**
	 * 分发GNSS采集器上报的原始码流...
	 */
	@Override
	public void processRptData(RptRawData data) throws Exception {
		if("N".equalsIgnoreCase(this.gnssCommProps.getNeedstart().trim())){
			logger.warn("[*@@*]HCGNSSParserService NOT need start, so discard data: "+data.toString());
			return;
		}
		// 添加到linkedqueue中，等待解析处理...
		boolean b = this.queue.offer(data);
		if(!b){
			logger.warn("[*!!*]Add data to queue failed: "+data.toString());
		}
	}
	
	/**
	 * 分配处理数据.
	 */
	private void dealRptRawData() {
		while (this.isWorkOn) {
			try {
				// 调用GNSS服务器的服务接口，进行数据转换...
				RptRawData rawData = this.queue.poll(60, TimeUnit.SECONDS);
				if(rawData == null){
					logger.debug("No raw data available now!");
					continue;
				} 
				if(RptRawData.INVALID_DEVEUI.equalsIgnoreCase(rawData.getDeveui())){
					continue;
				}
				byte[] bs = rawData.getPayload();
				bs = (bs == null) ? new byte[0] : bs;
				logger.info("Begin submit raw data to GNSS: "+rawData+", size="+bs.length+", data="+SecurityUtil.parseByte2HexStr(bs));
				String hostPort = this.deveui2HostMap.get(rawData.getDeveui());
				if(hostPort == null){
					logger.error("[*!!*]There is no dest host config for deveui: "+rawData.getDeveui());
					continue;
				}
				GnssCommService gnssCommSvr = this.host2CommSvrMap.get(hostPort);
				if(gnssCommSvr == null){
					logger.error("[*!!*]There is no dest host config for hostPort: "+hostPort);
					continue;
				}
				GnssRequest req = new GnssRequest();
				req.setRawData(rawData);
				if("Y".equalsIgnoreCase(this.gnssCommProps.getReturnrspdata().trim()) && 
				   bs.length < this.gnssCommProps.getReturnrspthreshold())
				{
					this.saveGnssData(gnssCommSvr.execCommand(req));
				} else {
					gnssCommSvr.execCommandIngoreRsp(req);
				}
				try{
					Thread.sleep(2);
				}catch(Throwable ignore){}
			} catch (Throwable t) {
				logger.error("dealRptRawData failed: " + t.getMessage(), t);
			}
		}
		logger.warn("Dealer thread exit: " + Thread.currentThread().getName());
	}
	
	/**
	 * 保存解析结果，并对外发布.
	 * 这个V1版本的数据（按华测的人说明，比较旧，不会再用了）
	 * @param ret
	 */
	public void saveGnssData(GnssParseResult ret) throws Exception {
		GnssRequest req = ret.getReq();
		String gnssRspStr = ret.getRsp().getRspData();
		String devTypeId = req.getDevType();
		DevTypeModel dtm = this.cacheManager.getDevTypeModel(devTypeId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTypeId);
			return;
		}
		GNEHResultData gneData = GNEHResultData.parseRspData(gnssRspStr);
		if(gneData == null){
			logger.error("[*!!*]Can not parse gnss result data: "+gnssRspStr);
			return;			
		}
		Map<String, Object> mdata = JsonUtils.beanToHashMap(gneData);
		DevMeteData meteData = new DevMeteData();
		meteData.setAppeui(req.getAppEui());
		meteData.setDeveui(req.getDevEui());
		meteData.setDevname(req.getDevName());
		meteData.setDevtype(devTypeId);
		meteData.setPort(req.getPortStr());
		meteData.setProjectid(req.getProjectId());
		meteData.setRpttime(new Timestamp(System.currentTimeMillis()));
		meteData.setMdata(mdata);
		// 持久化数据.
		this.parserContainer.getPersisService().saveDevMeteData(meteData, dtm.getBase().getTbname());
		// 5.构造广播数据，进行redis发布...
		String pubMsg = JsonUtils.toJson(meteData);
		this.parserContainer.getUpdataPublisher().publishMessage(pubMsg);
		logger.info("[*^_^*]Publish metedata message ok: "+pubMsg);
	}

	/**
	 * 异步接收华测GNSS的解析结果，持久化数据并发布（对齐华测新版解析后的数据格式）.
	 */
	@Override
	public void onParseResultReceive(Object msg) {
		String gnssRspStr = msg.toString();
		GNEHResultDataV2 gneData = GNEHResultDataV2.parseRspData(gnssRspStr);
		if(gneData == null){
			logger.error("[*!!*]Can not parse gnss result data: "+gnssRspStr);
			return;			
		}
		String stationId = gneData.getStationid();
		SensorDevEuiPV pv = this.cacheManager.getSensorDevEuiPV(stationId);
		if(pv == null){
			logger.error("[*@@*]Can not get sensorid--deveui config data: "+stationId);
			return;	
		}
		DevBase dev = this.cacheManager.getDev(pv.getDeveui());
		if(dev == null){
			logger.error("[*@@*]Can not get dev data for sensor: "+stationId);
			return;	
		}
		DevTypeModel dtm = this.cacheManager.getDevTypeModel(dev.getDevType());
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+dev.getDevType());
			return;
		}
		Map<String, Object> mdata = JsonUtils.beanToHashMap(gneData);
		DevMeteData meteData = new DevMeteData();
		meteData.setAppeui(dev.getAppEui());
		meteData.setDeveui(dev.getDevEui());
		meteData.setDevname(dev.getDevName());
		meteData.setDevtype(dev.getDevType());
		meteData.setPort("0");
		meteData.setProjectid(dev.getProjectId());
		meteData.setRpttime(new Timestamp(System.currentTimeMillis()));
		meteData.setMdata(mdata);
		// 持久化数据.
		try {
			this.parserContainer.getPersisService().saveDevMeteData(meteData, dtm.getBase().getTbname());
			// 构造广播数据，进行redis发布...
			String pubMsg = JsonUtils.toJson(meteData);
			this.parserContainer.getUpdataPublisher().publishMessage(pubMsg);
			logger.info("[*^_^*]Publish parse result data ok: " + pubMsg);
		}catch(Exception ex){
			logger.error("onParseResultReceive process error: "+msg, ex);
		}
	}
	
}
